#include "Avalon/ServiceLocator/ServiceChannelFactory.hpp"
#pragma warning(disable : 4003)
#include <libjson.h>
#pragma warning(default : 4003)
#include "Avalon/IO/ChannelAdapterServerConnection.hpp"
#include "Avalon/IO/DelimitedReader.hpp"
#include "Avalon/IO/DelimitedWriter.hpp"
#include "Avalon/IO/SizeDeclarativeReader.hpp"
#include "Avalon/IO/SizeDeclarativeWriter.hpp"
#include "Avalon/Network/TcpClientSocketChannel.hpp"
#include "Avalon/Network/TcpServerSocket.hpp"
#include "Avalon/Serialization/BinaryDataShuttleFactory.hpp"
#include "Avalon/Serialization/JsonDataShuttleFactory.hpp"

using namespace Avalon;
using namespace Avalon::Codecs;
using namespace Avalon::IO;
using namespace Avalon::Network;
using namespace Avalon::Serialization;
using namespace Avalon::ServiceLocator;
using namespace boost;
using namespace libjson;
using namespace std;

namespace {
  Carry<Reader> DelimitedReaderBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    Carry<Reader> baseReader = builder.BuildReader(jsonNode["base"].write());
    int delimiter = jsonNode["delimiter"].as_int();
    return new DelimitedReader(baseReader, static_cast<char>(delimiter));
  }

  Carry<Writer> DelimitedWriterBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    Carry<Writer> baseWriter = builder.BuildWriter(jsonNode["base"].write());
    int delimiter = jsonNode["delimiter"].as_int();
    return new DelimitedWriter(baseWriter, static_cast<char>(delimiter));
  }

  Carry<Reader> SizeDeclarativeReaderBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    Carry<Reader> baseReader = builder.BuildReader(jsonNode["base"].write());
    return new SizeDeclarativeReader(baseReader);
  }

  Carry<Writer> SizeDeclarativeWriterBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    Carry<Writer> baseWriter = builder.BuildWriter(jsonNode["base"].write());
    return new SizeDeclarativeWriter(baseWriter);
  }

  Carry<Channel> WrapperChannelBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    Carry<Channel> baseChannel = builder.BuildChannel(
      jsonNode["base"].write());
    Carry<Reader> wrapperReader;
    JSONNode::iterator readerIterator = jsonNode.find("reader");
    if(readerIterator != jsonNode.end()) {
      JSONNode& readerNode = *readerIterator;
      try {
        builder.Push(baseChannel->GetReader());
        wrapperReader = builder.BuildReader(readerNode.write());
      } catch(std::exception&) {
        Handle<Channel> channelHandler(baseChannel);
        builder.PopReader();
        BOOST_RETHROW;
      }
    } else {
      wrapperReader = baseChannel->GetReader();
    }
    Carry<Writer> wrapperWriter;
    JSONNode::iterator writerIterator = jsonNode.find("writer");
    if(writerIterator != jsonNode.end()) {
      JSONNode& writerNode = *writerIterator;
      try {
        builder.Push(baseChannel->GetWriter());
        wrapperWriter = builder.BuildWriter(writerNode.write());
      } catch(std::exception&) {
        Handle<Channel> channelHandler(baseChannel);
        builder.PopWriter();
        BOOST_RETHROW;
      }
    } else {
      wrapperWriter = baseChannel->GetWriter();
    }
    Channel* wrapperChannel = new BasicChannel(baseChannel,
      baseChannel->GetConnection(), wrapperReader, wrapperWriter);
    return wrapperChannel;
  }

  class WrapperConnectionFactory : public ChannelFactory {
    public:
      WrapperConnectionFactory(const ServiceChannelFactory& factory,
          const JSONNode& node)
          : m_factory(factory),
            m_node(node) {}

      virtual Channel* Create(Carry<Connection> connection,
          Carry<Reader> reader, Carry<Writer> writer) {
        Carry<Reader> wrapperReader;
        JSONNode::iterator readerIterator = m_node.find("reader");
        ServiceChannelBuilder builder(&m_factory);
        if(readerIterator != m_node.end()) {
          builder.Push(reader);
          wrapperReader = builder.BuildReader((*readerIterator).write());
        } else {
          wrapperReader = reader;
        }
        Carry<Writer> wrapperWriter;
        JSONNode::iterator writerIterator = m_node.find("writer");
        if(writerIterator != m_node.end()) {
          builder.Push(writer);
          wrapperWriter = builder.BuildWriter((*writerIterator).write());
        } else {
          wrapperWriter = writer;
        }
        Channel* wrapperChannel = new BasicChannel(connection, wrapperReader,
          wrapperWriter);
        return wrapperChannel;
      }

    private:
      ServiceChannelFactory m_factory;
      JSONNode m_node;
  };

  Carry<Connection> WrapperConnectionBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    Carry<Connection> baseConnection = builder.BuildConnection(
      jsonNode["base"].write());
    ChannelAdapterServerConnection* wrapperConnection =
      new ChannelAdapterServerConnection(static_cast<ServerConnection*>(
      &*baseConnection), new WrapperConnectionFactory(
      builder.GetServiceChannelFactory(), jsonNode));
    return wrapperConnection;
  }

  Carry<Channel> TcpChannelBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    string hostname = jsonNode["hostname"].as_string();
    unsigned short port = static_cast<unsigned short>(
      jsonNode["port"].as_int());
    TcpClientSocketChannel* channel = new TcpClientSocketChannel(
      IpAddress(hostname, port));
    return channel;
  }

  Carry<Connection> TcpConnectionBuilder(ServiceChannelBuilder builder,
      const string& node) {
    JSONNode jsonNode = parse(node);
    string hostname = jsonNode["hostname"].as_string();
    unsigned short port = static_cast<unsigned short>(
      jsonNode["port"].as_int());
    TcpServerSocket* connection = new TcpServerSocket(
      IpAddress(hostname, port));
    return connection;
  }

  Carry<DataShuttleFactory> BinaryDataShuttleFactoryBuilder(
      ServiceChannelBuilder builder, const string& node) {
    return new BinaryDataShuttleFactory();
  }

  Carry<DataShuttleFactory> JsonDataShuttleFactoryBuilder(
      ServiceChannelBuilder builder, const string& node) {
    return new JsonDataShuttleFactory();
  }
}

ServiceChannelBuilder::ServiceChannelBuilder(
    ServiceChannelFactory* serviceChannelFactory)
    : m_serviceChannelFactory(serviceChannelFactory) {}

Carry<Encoder> ServiceChannelBuilder::BuildEncoder(
    const string& encoderNode) {
  return m_serviceChannelFactory->m_encoders.Create(*this, encoderNode,
    &m_encoderStack);
}

Carry<Decoder> ServiceChannelBuilder::BuildDecoder(
    const string& decoderNode) {
  return m_serviceChannelFactory->m_decoders.Create(*this, decoderNode,
    &m_decoderStack);
}

Carry<Connection> ServiceChannelBuilder::BuildConnection(
    const string& connectionNode) {
  return m_serviceChannelFactory->m_connections.Create(*this, connectionNode,
    &m_connectionStack);
}

Carry<Reader> ServiceChannelBuilder::BuildReader(
    const string& readerNode) {
  return m_serviceChannelFactory->m_readers.Create(*this, readerNode,
    &m_readerStack);
}

Carry<Writer> ServiceChannelBuilder::BuildWriter(
    const string& writerNode) {
  return m_serviceChannelFactory->m_writers.Create(*this, writerNode,
    &m_writerStack);
}

Carry<Channel> ServiceChannelBuilder::BuildChannel(
    const string& channelNode) {
  return m_serviceChannelFactory->m_channels.Create(*this, channelNode,
    &m_channelStack);
}

Carry<DataShuttleFactory> ServiceChannelBuilder::BuildDataShuttleFactory(
    const string& shuttleNode) {
  return m_serviceChannelFactory->m_dataShuttleFactories.Create(*this,
    shuttleNode, &m_dataShuttleFactoryStack);
}

void ServiceChannelBuilder::Push(Carry<Reader> reader) {
  m_readerStack.push_front(reader);
}

void ServiceChannelBuilder::PopReader() {
  m_readerStack.pop_front();
}

void ServiceChannelBuilder::Push(Carry<Writer> writer) {
  m_writerStack.push_front(writer);
}

void ServiceChannelBuilder::PopWriter() {
  m_writerStack.pop_front();
}

const ServiceChannelFactory&
    ServiceChannelBuilder::GetServiceChannelFactory() {
  return *m_serviceChannelFactory;
}

map<string, ServiceChannelFactory::EncoderBuilder>
    ServiceChannelFactory::GetDefaultEncoderBuilders() {
  return map<string, EncoderBuilder>();
}

map<string, ServiceChannelFactory::DecoderBuilder>
    ServiceChannelFactory::GetDefaultDecoderBuilders() {
  return map<string, DecoderBuilder>();
}

map<string, ServiceChannelFactory::ConnectionBuilder>
    ServiceChannelFactory::GetDefaultConnectionBuilders() {
  map<string, ConnectionBuilder> connectionBuilders;
  connectionBuilders.insert(make_pair("tcp", &TcpConnectionBuilder));
  connectionBuilders.insert(make_pair("wrapper", &WrapperConnectionBuilder));
  return connectionBuilders;
}

map<string, ServiceChannelFactory::ReaderBuilder>
    ServiceChannelFactory::GetDefaultReaderBuilders() {
  map<string, ReaderBuilder> readerBuilders;
  readerBuilders.insert(make_pair("size_declarative",
    &SizeDeclarativeReaderBuilder));
  readerBuilders.insert(make_pair("delimited", &DelimitedReaderBuilder));
  return readerBuilders;
}

map<string, ServiceChannelFactory::WriterBuilder>
    ServiceChannelFactory::GetDefaultWriterBuilders() {
  map<string, WriterBuilder> writerBuilders;
  writerBuilders.insert(make_pair("size_declarative",
    &SizeDeclarativeWriterBuilder));
  writerBuilders.insert(make_pair("delimited", &DelimitedWriterBuilder));
  return writerBuilders;
}

map<string, ServiceChannelFactory::ChannelBuilder>
    ServiceChannelFactory::GetDefaultChannelBuilders() {
  map<string, ChannelBuilder> channelBuilders;
  channelBuilders.insert(make_pair("tcp", &TcpChannelBuilder));
  channelBuilders.insert(make_pair("wrapper", &WrapperChannelBuilder));
  return channelBuilders;
}

map<string, ServiceChannelFactory::DataShuttleFactoryBuilder>
    ServiceChannelFactory::GetDefaultDataShuttleFactoryBuilders() {
  map<string, DataShuttleFactoryBuilder> dataShuttleFactoryBuilders;
  dataShuttleFactoryBuilders.insert(make_pair("binary",
    &BinaryDataShuttleFactoryBuilder));
  dataShuttleFactoryBuilders.insert(make_pair("json",
    &JsonDataShuttleFactoryBuilder));
  return dataShuttleFactoryBuilders;
}

ServiceChannelFactory::ServiceChannelFactory()
    : m_encoders(GetDefaultEncoderBuilders()),
      m_decoders(GetDefaultDecoderBuilders()),
      m_connections(GetDefaultConnectionBuilders()),
      m_readers(GetDefaultReaderBuilders()),
      m_writers(GetDefaultWriterBuilders()),
      m_channels(GetDefaultChannelBuilders()),
      m_dataShuttleFactories(GetDefaultDataShuttleFactoryBuilders()) {}

ServiceChannelFactory::ServiceChannelFactory(
    const map<string, EncoderBuilder>& encoders,
    const map<string, DecoderBuilder>& decoders,
    const map<string, ConnectionBuilder>& connections,
    const map<string, ReaderBuilder>& readers,
    const map<string, WriterBuilder>& writers,
    const map<string, ChannelBuilder>& channels,
    const map<string, DataShuttleFactoryBuilder>& dataShuttleFactories)
    : m_encoders(encoders),
      m_decoders(decoders),
      m_connections(connections),
      m_readers(readers),
      m_writers(writers),
      m_channels(channels),
      m_dataShuttleFactories(dataShuttleFactories) {}

ServiceChannelFactory::~ServiceChannelFactory() {}

Encoder* ServiceChannelFactory::CreateEncoder(const string& encoderNode) {
  ServiceChannelBuilder builder(this);
  return &*m_encoders.Create(builder, encoderNode, &builder.m_encoderStack);
}

Decoder* ServiceChannelFactory::CreateDecoder(const string& decoderNode) {
  ServiceChannelBuilder builder(this);
  return &*m_decoders.Create(builder, decoderNode, &builder.m_decoderStack);
}

Connection* ServiceChannelFactory::CreateConnection(
    const string& connectionNode) {
  ServiceChannelBuilder builder(this);
  return &*m_connections.Create(builder, connectionNode,
    &builder.m_connectionStack);
}

Reader* ServiceChannelFactory::CreateReader(const string& readerNode) {
  ServiceChannelBuilder builder(this);
  return &*m_readers.Create(builder, readerNode, &builder.m_readerStack);
}

Writer* ServiceChannelFactory::CreateWriter(const string& writerNode) {
  ServiceChannelBuilder builder(this);
  return &*m_writers.Create(builder, writerNode, &builder.m_writerStack);
}

Channel* ServiceChannelFactory::CreateChannel(const string& channelNode) {
  ServiceChannelBuilder builder(this);
  return &*m_channels.Create(builder, channelNode, &builder.m_channelStack);
}

DataShuttleFactory* ServiceChannelFactory::CreateDataShuttleFactory(
    const string& shuttleNode) {
  ServiceChannelBuilder builder(this);
  return &*m_dataShuttleFactories.Create(builder, shuttleNode,
    &builder.m_dataShuttleFactoryStack);
}

template<typename T>
ServiceChannelFactory::Component<T>::Component(
    const map<string, Builder>& builders)
    : m_builders(builders) {}

template<typename T>
Carry<T> ServiceChannelFactory::Component<T>::Create(
    ServiceChannelBuilder builder, const string& node,
    deque<Carry<T> >* stack) {
  JSONNode jsonNode = parse(node);
  string type = jsonNode["type"].as_string();
  if(type == "pop") {
    Carry<T> component = stack->front();
    stack->pop_front();
    return component;
  }
  typename map<string, Builder>::const_iterator builderIterator =
    m_builders.find(type);
  if(builderIterator == m_builders.end()) {
    BOOST_THROW_EXCEPTION(runtime_error("Builder not found: " + type));
  }
  return builderIterator->second(builder, node);
}
